﻿using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;

namespace msilgc.common
{
    public static class NativeMethodHelperFactory {
        public static INativeMethodHelpers Create() {
            switch (Environment.OSVersion.Platform) {
                case PlatformID.Win32S:
                case PlatformID.Win32Windows:
                case PlatformID.Win32NT:
                    return new WindowsNativeMethodHelper();
                case PlatformID.Unix:
                case PlatformID.MacOSX:
                    return new UnixNativeMethodHelper();
                default:
                    throw new SystemException("platform is not supported");
            }
        }
    }
    public interface INativeMethodHelpers {
        Delegate GetMethodHandle(string libraryName, string procName, Type delegateType);
        void ThrowForWin32Error();
        DirectoryInfo CreateTemporaryFolder();
    }
    public class UnixNativeMethodHelper : INativeMethodHelpers {
        public Delegate GetMethodHandle(string libraryName, string procName, Type delegateType) {
            throw new NotImplementedException();
        }

        public void ThrowForWin32Error() {
            throw new NotImplementedException();
        }

        public DirectoryInfo CreateTemporaryFolder() {
            throw new NotImplementedException();
        }
    }
    public class WindowsNativeMethodHelper : INativeMethodHelpers {
        [DllImport("kernel32.dll")]
        internal static extern IntPtr LoadLibrary(String dllname);

        [DllImport("kernel32.dll")]
        internal static extern IntPtr GetProcAddress(IntPtr hModule, String procname);

        [DllImport("kernel32.dll")]
        internal static extern int GetLastError();
        
        private static string GetLastErrorAsString() {
            return new Win32Exception(GetLastError()).Message;
        }

        public Delegate GetMethodHandle(string libraryName, string procName, Type delegateType) {
            IntPtr dllHandle = LoadLibrary(libraryName);
            if (dllHandle == IntPtr.Zero)
            {
                string errMessage = GetLastErrorAsString();
                throw new Exception("failed to load library: " + libraryName + ": " + errMessage);
            }
            IntPtr procPtr = GetProcAddress(dllHandle, procName);
            if (procPtr == IntPtr.Zero) {
                throw new Exception("failed to load method: " + procName);
            }
            return Marshal.GetDelegateForFunctionPointer(procPtr, delegateType);
        }

        public void ThrowForWin32Error() {
            var err = GetLastError();
            if (err != 0) {
                throw new Win32Exception(err);
            }
        }

        public DirectoryInfo CreateTemporaryFolder() {
            string tempPath = CudaSettings.SystemTempFolder;
            tempPath = Path.Combine(tempPath, Guid.NewGuid().ToString());
            DirectoryInfo folder = new DirectoryInfo(tempPath);
            folder.Create();
            return folder;
        }
    }
}